package org.simpleframework.inject;

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.Set;

import org.simpleframework.core.BeanContainer;
import org.simpleframework.inject.annotation.Autowired;
import org.simpleframework.util.ClassUtil;
import org.simpleframework.util.ValidationUtil;

/**
 * 提供依赖注入的服务
 * 
 * @author icanci
 * @date Created in 2021/02/06 16:18
 * @version 1.0
 */
@Slf4j
public class DependencyInjector {
    /**
     * 获取容器
     */
    private static BeanContainer beanContainer;

    /**
     * 构造函数
     */
    public DependencyInjector() {
        beanContainer = BeanContainer.getInstance();
    }

    /**
     * 执行 IOC
     */
    public void doIoc() {
        // 遍历Bean容器中所有的Class对象
        if (ValidationUtil.isEmpty(beanContainer.getClasses())) {
            log.warn("empty class set in BeanContainer");
            return;
        }
        // 遍历Class对象中的所有的成员变量
        for (Class<?> clazz : beanContainer.getClasses()) {
            Field[] declaredFields = clazz.getDeclaredFields();
            if (ValidationUtil.isEmpty(declaredFields)) {
                continue;
            }
            // 找出被Autowired标记的成员变量
            for (Field declaredField : declaredFields) {
                if (declaredField.isAnnotationPresent(Autowired.class)) {
                    Autowired autowired = declaredField.getAnnotation(Autowired.class);
                    String autowiredValue = autowired.value();
                    // 获取这些成员变量的类型
                    Class<?> fieldClass = declaredField.getType();
                    // 获取这些成员变量的类型在容器里面的实例
                    Object fieldValue = getFieldInstance(fieldClass, autowiredValue);
                    if (null == fieldValue) {
                        throw new RuntimeException("unable to inject relevant type ,target fieldClass is:" + fieldClass.getName());
                    } else {
                        // 通过反射将对应的成员变量实例注入到成员变量所在类的实例里面
                        Object targetBean = beanContainer.getBean(clazz);
                        ClassUtil.setField(declaredField, targetBean, fieldValue, true);
                    }
                }
            }
        }
    }

    /**
     * 根据class在beanContainer里获取其实例或者实现类
     * @param fieldClass class
     * @param autowiredValue 注解实例
     * @return 返回实现类或者null
     */
    private static Object getFieldInstance(Class<?> fieldClass, String autowiredValue) {
        Object fieldValue = beanContainer.getBean(fieldClass);
        if (null != fieldValue) {
            return fieldValue;
        } else {
            Class<?> implementedClass = getImplementClass(fieldClass, autowiredValue);
            if (null != implementedClass) {
                return beanContainer.getBean(implementedClass);
            } else {
                return null;
            }
        }
    }

    /**
     * 获取接口的实现类
     * @param fieldClass 接口
     * @param autowiredValue 注解名称
     * @return 实现类
     */
    private static Class<?> getImplementClass(Class<?> fieldClass, String autowiredValue) {
        Set<Class<?>> classSet = beanContainer.getClassBySuper(fieldClass);
        if (!ValidationUtil.isEmpty(classSet)) {
            if (ValidationUtil.isEmpty(autowiredValue)) {
                if (classSet.size() == 1) {
                    return classSet.iterator().next();
                } else {
                    //如果多于两个实现类且用户未指定其中一个实现类，则抛出异常
                    throw new RuntimeException("multiple implemented classes for " + fieldClass.getName() + " please set @Autowired value to pick one");
                }
            }
        } else {
            for (Class<?> clazz : classSet) {
                if (autowiredValue.equals(clazz.getSimpleName())) {
                    return clazz;
                }
            }
        }
        return null;
    }

}
